
rem
bbdoc: Layered Render system.
about: Uses layers to order the drawing of renderables.
Uses QuadTree and Camera for fast entity lookup.
endrem
Type TRenderSystem Extends TEntityProcessingSystem' Abstract

	'an index in this bag contains a layer bag. a layer bag contains renderables by entity id.
	'index = render order, 0 is first
	Field _layers:TBag
	
	'settings for layers, by index.
	Field _layerSettings:TBag
	
	Field _renderFormMapper:TComponentMapper
	Field _transformMapper:TComponentMapper
	Field _positionMapper:TComponentMapper
	Field _colorMapper:TComponentMapper

	Field _quadTreeSystem:TQuadTreeSystem
	Field _cameraSystem:TCameraSystem
	
	'debug counter
	Field _totalRendered:Int

	
	
	Method Initialize()
		_layers = New TBag
		_layerSettings = New TBag
		_renderFormMapper = TComponentMapper.Create(New TRenderFormComponent, _world)
		_transformMapper = TComponentMapper.Create(New TTransformComponent, _world)
		_positionMapper = TComponentMapper.Create(New TPositionComponent, _world)
		_colorMapper = TComponentMapper.Create(New TColorComponent, _world)
		
		RegisterComponent(New TRenderFormComponent)
		RegisterComponent(New TTransformComponent)
		RegisterComponent(New TPositionComponent)
		RegisterComponent(New TColorComponent)
	End Method

	

	'not used. we're processing renderables.
	Method ProcessEntity(e:TEntity)
	End Method
	
	
	'is override
	Method Process()
		If CheckProcessing() = True
			Begin()
			
			_totalRendered = 0
			
			If Not _quadTreeSystem Or Not _cameraSystem
				RuntimeError "Camera or QuadTree system not set!"
			End If
			
			Local visibleEntities:TBag = _quadTreeSystem.GetEntities(_cameraSystem.GetViewport())
			For Local layerIndex:Int = 0 To _layers.GetSize() - 1
				'the layer bag does not contain entities,
				'but renderables for this layer (by entity id)
				
				'see if settings for layer exists. skip this index if not
				Local layerSettings:TRenderLayerSettings = TRenderLayerSettings(_layerSettings.Get(layerIndex))
				If layerSettings
					'skip if not visible
					If layerSettings.GetVisible() = False Then Continue
				
					'apply layer settings	
					SetAlpha(layerSettings.GetAlpha())
					
					'get bag with renderables for this layer and process
					Local layerBag:TBag = TBag(_layers.Get(layerIndex))
					ProcessRenderables(layerBag, visibleEntities)
				EndIf
			Next
			Stop()
		End If
	End Method
		
	
	Method ProcessRenderables(renderables:TBag, visibleEntities:TBag)
	
		'renderables' contains the renderables to draw.
		'visibleentities' contains the entities that are actually visible
		'by the camera.
		'renderables and entities share ids.		
		'entity is not rendered when its visible flag is false.
		
		Local p:TPositionComponent
		Local renderable:TRenderable
		
		For Local index:Int = 0 To renderables.GetSize() - 1
		
			'skip if no renderable at this position in the bag.
			If renderables.Get(index) = Null Then Continue
			
			renderable = TRenderable(renderables.Get(index))
			'skip if entity is not visible
			If renderable.GetVisible() = False Then Continue
			'skip if entity cannot be in this bag
			If index >= visibleEntities.GetSize() Then Continue
			'skip if entity is not in this bag
			If visibleEntities.Get(index) = Null Then Continue
			
			'let's go!
			p = renderable._position
			'calculate render position with tween
			p.Interpolate(_world.GetTween())
			renderable.Render()
			
			'debug counter
			_totalRendered:+1
		Next
		
	End Method


	rem
	Called when the entity is added to the system.
	A renderable for this entity is created. The image for the renderable is loaded from the
	resource manager according to the renderform name. This should be the same as
	the image label in the resource manager.
	endrem
	Method Added(e:TEntity)
	
		'create a renderable for this entity
		Local form:TRenderFormComponent = TRenderFormComponent(_renderFormMapper.Get(e))
		Local r:TRenderable = TRenderable.Create(_world, e, form.GetImageName(), form.GetImageGroup(), form.GetImageFrame())
		If Not r Then RuntimeError("No renderable found, cannot add this entity to rendersystem!")
		
		'add it to render layer.
		Self.AddRenderableToLayer(e, r)
	End Method
	

	
	Method Removed(e:TEntity)
		Local layerIndex:Int = TRenderFormComponent(_renderFormMapper.Get(e)).GetLayer()
		Local layerBag:TBag = TBag(_layers.Get(layerIndex))
		layerBag.Set(e.GetId(), Null)
	End Method

	
	
	Method AddRenderableToLayer(e:TEntity, r:TRenderable)
	
		'get the layer index for this renderable.
		Local layerIndex:Int = TRenderFormComponent(_renderFormMapper.Get(e)).GetLayer()
		
		'does this layer exist? create if not.
		If Not _layerSettings.Get(layerIndex)
			_layerSettings.Set(layerIndex, TRenderLayerSettings.Create(layerIndex))
			_layers.Set(layerIndex, New TBag)
		EndIf
		
		'get the layerbag for the layer the renderable must belong to.
		Local layerBag:TBag = TBag(_layers.Get(layerIndex))
		
		If Not layerBag.Contains(r) Then layerBag.Set(e.GetId(), r)
	End Method
	
	
	
	'returns the index of added layer
	Method AddRenderLayer:Int()
		Local newIndex:Int = _layersettings.GetSize()
		Self.CreateRenderLayer(newIndex)
		Return newIndex
	End Method
	
	
	
	Method CreateRenderLayer(index:Int)
		'already existing?
		If _layerSettings.Get(index) Then Return
		
		'create settings and renderable layer
		_layerSettings.Set(index, TRenderLayerSettings.Create(index))
		_layers.Set(index, New TBag)
	End Method
	
	
	rem
	bbdoc: Returns renderable for passed entity.
	endrem
	Method GetRenderable:TRenderable(e:TEntity)
		Local layerIndex:Int = TRenderFormComponent(_renderFormMapper.Get(e)).GetLayer()
		Local layerBag:TBag = TBag(_layers.Get(layerIndex))
		If layerBag Then Return TRenderable(layerBag.Get(e.GetId()))
		RuntimeError "render layer for entity not found!"
	End Method
	
	
	
	Method GetLayerSettingsByIndex:TRenderLayerSettings(index:Int)
		Return TRenderLayerSettings(_layerSettings.Get(index))
	End Method

	
	
	Method GetLayerSettings:TBag()
		Return _layerSettings
	End Method
	
	
	
	Method GetLayers:TBag()
		Return _layers
	End Method
	
		
	
	Method SetQuadTree(s:TQuadTreeSystem)
		_quadTreeSystem = s
	End Method


	Method SetCamera(s:TCameraSystem)
		_cameraSystem = s
	End Method
	
	
	
	'creates and reads layer settings from stream
	Method Read(s:TStream)
		Local settings:TRenderLayerSettings = New TRenderLayerSettings
		settings.Read(s)
		
		'add it to render system, and the layer
		_layerSettings.Set(settings.index, settings)
		_layers.Set(settings.index, New TBag)
	End Method
	
	
	
	'writes all render layer settings to stream
	Method WriteSettings(s:TStream)
		For Local index:Int = 0 To _layerSettings.GetSize() - 1
			If _layerSettings.Get(index)
				TRenderLayerSettings(_layerSettings.Get(index)).Write(s)
			EndIf
		Next
	End Method

		
	
	Method DebugText(y:Int)
		DrawText("Entities Rendered: " + _totalRendered, 0, y)
		DrawText("Layers: " + _layers.GetSize(), 0, y + 15)
	End Method
	
End Type
